package com.qd.panda.gas.station.ejiayou;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.math.Money;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.JacksonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.carpark.calculate.CalculatePriceDTO;
import com.qd.common.panda.domain.entity.carpark.calculate.CalculatePriceDetailDTO;
import com.qd.common.panda.domain.enums.GasStationCompanyNameEnum;
import com.qd.common.panda.domain.enums.PreferentialCategoryEnum;
import com.qd.common.sys.domain.entity.log.SendLogDTO;
import com.qd.panda.gas.station.base.BaseHttpResponseUtil;
import com.qd.panda.gas.station.base.GasHttpService;
import com.qd.panda.gas.station.ejiayou.config.EGasConfig;
import com.qd.panda.gas.station.ejiayou.req.*;
import com.qd.panda.gas.station.ejiayou.resp.station.EGasStationDTO;
import com.qd.panda.gas.station.ejiayou.util.EGasSignUtil;
import com.qd.panda.gas.station.req.*;
import com.qd.panda.gas.station.resp.PayRespDTO;
import com.qd.panda.gas.station.shell.resp.ShellResponseDTO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Supplier;

/**
 * 和易加油接口对接
 *
 * @author sjk
 * @create 2023-02-09 15:53
 * @since 1.0.0
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class EGasHttpServiceImpl extends GasHttpService {
    /**
     * 获取站点列表
     */
    public static final String STATIONS_URL = "/oreo/ejiayou_open_api/stations/v2/{platformName}/{sign}/{timestamp}";
    /**
     * 获取站点详情
     */
    public static final String STATION_DETAIL_URL = "/oreo/ejiayou_open_api/platform/station/prices/v1/{platformName}/{userPhone}/{stationId}/{sign}/{timestamp}";
    /**
     * 计算订单
     */
    public static final String COMPUTE_URL = "/oreo/ejiayou_open_api/platform/order/compute/v1/{platformName}/{sign}/{timestamp}";
    /**
     * 创建订单
     */
    private static final String PAY_URL = "/oreo/ejiayou_open_api/orders/v2/{platformName}/{sign}/{timestamp}";
    /**
     * 财务对账
     */
    private static final String ORDERS_URL = "/oreo/ejiayou_open_api/platform/orders/v2/{platformName}/{sign}/{timestamp}";
    /**
     * 取消订单
     */
    private static final String CANCEL_URL = "/oreo/ejiayou_open_api/platform/orders/cancel/v1/{platformName}/{sign}/{timestamp}";
    /**
     * 支付回调
     */
    private static final String PAY_NOTIFY_URL = "/oreo/ejiayou_open_api/orders/v1/payments/{orderSign}/{platformName}/{sign}/{timestamp}";
    /**
     * 退款
     */
    private static final String REFUND_NOTIFY_URL = "/oreo/ejiayou_open_api/platform/refund/v1/{platformName}/{userPhone}/{orderId}/{sign}/{timestamp}?reason={reason}";
    /**
     * 个人订单查询
     */
    private static final String HISTORY_URL = "/oreo/ejiayou_open_api/orders/histroy/{userPhone}/{platformName}/{sign}/{timestamp}";
    /**
     * 查询优惠券
     */
    private static final String QUERY_URL = "/oreo/ejiayou_open_api/merchandise/query/{userPhone}/{platformName}/{sign}/{timestamp}";
    /**
     * 发放优惠券
     */
    private static final String MERCHANDISE_URL = "/oreo/ejiayou_open_api/platform/merchandise/v1/{platformName}/{userPhone}/{sourceId}/{sign}/{timestamp}";
    /**
     * 兑换优惠券
     */
    private static final String MERCHANDISE_EXCHANGE_URL = "/oreo/ejiayou_open_api/platform/merchandise/exchange/v1/{platformName}/{sign}/{timestamp}";


    private final EGasConfig eGasConfig;

    private final RestTemplate restTemplate;

    private final ObjectMapper objectMapper;


    @Override
    public String stationCompanyId() {
        return GasStationCompanyNameEnum.E_JIA_YOU.getCode();
    }

    /**
     * 计算订单
     *
     * @param base 支付参数
     * @return oilMass    BigDecimal	油量，单位升，保留两位小数
     * payAmount	BigDecimal	用户本单应支付的金额，保留两位小数
     * serviceFee	BigDecimal	服务费，保留两位小数（服务费规则具体咨询易能时代运营）
     * discountAmount	BigDecimal	直降优惠金额（含服务费计算），保留两位小数
     * couponAmount	BigDecimal	优惠券（若有使用优惠券）优惠金额，保留两位小数
     * discountPrice	string	最终享受单价
     * countryPrice	BigDecimal	国家价
     * stationPrice	BigDecimal	挂牌价
     * payUnitPrice	BigDecimal	用户享受单价
     */
    @SneakyThrows
    @Override
    public CalculatePriceDTO compute(ComputeReqDTO base) {
        EPayReqDTO ePayReqDTO = new EPayReqDTO();
        ePayReqDTO.setStationId(Integer.parseInt(base.getStationId()))
                .setOilgunCode(base.getNozzleNumber())
                .setTotalAmount(base.getTotalAmount().toString())
                .setPhoneNumber(base.getMobile());
        String url = eGasConfig.getUrl() + COMPUTE_URL;
        Map<String, String> map = createBaseMap();
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        signMap.clear();
        log.info("易加油,计算订单: {}", ePayReqDTO);
        final SendLogDTO sendLogDTO = base.getSendLogDTO();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.POST.name());
            sendLogDTO.setReqParam(ePayReqDTO.toString());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayReqDTO);
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayReqDTO.freeData();
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return buildCalculatePrice(base.getTotalAmount(), jsonNode);
        }
        return null;
    }

    /**
     * 创建订单
     *
     * @param base 订单参数
     * @return 订单ID
     */
    @SneakyThrows
    @Override
    public PayRespDTO pay(PayReqDTO base) {
        EPayReqDTO ePayReqDTO = new EPayReqDTO();
        ePayReqDTO.setStationId(Integer.parseInt(base.getStationId()))
                .setOilgunCode(base.getNozzleNumber())
                .setTotalAmount(base.getTotalAmount().toString())
                .setPhoneNumber(base.getUserId())
                .setOutOrderSign(base.getThirdPartyOrderId());
        String url = eGasConfig.getUrl() + PAY_URL;
        Map<String, String> map = createBaseMap();
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        signMap.clear();
        log.info("易加油,创建订单: {}", objectMapper.writeValueAsString(ePayReqDTO));
        final SendLogDTO sendLogDTO = base.getSendLogDTO();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.POST.name());
            sendLogDTO.setReqParam(ePayReqDTO.toString());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayReqDTO);
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayReqDTO.freeData();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            final JsonNode data = Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
            PayRespDTO payRespDTO = new PayRespDTO();
            if (null != data.get("orderSign")) {
                payRespDTO.setOrderId(data.get("orderSign").asText());
            }
            if (null != data.get("oilMass")) {
                JsonNode oilMassNode = data.get("oilMass");
                if (oilMassNode.isBigDecimal()) {
                    payRespDTO.setOilMass(oilMassNode.decimalValue());
                } else {
                    payRespDTO.setOilMass(new Money(oilMassNode.asText()).getAmount());
                }
            }
            if (null != data.get("payAmount")) {
                JsonNode payAmountNode = data.get("payAmount");
                if (payAmountNode.isBigDecimal()) {
                    payRespDTO.setPayAmount(payAmountNode.decimalValue());
                } else {
                    payRespDTO.setPayAmount(new Money(payAmountNode.asText()).getAmount());
                }
            }
            if (null != data.get("discountAmount")) {
                JsonNode discountAmountNode = data.get("discountAmount");
                if (discountAmountNode.isBigDecimal()) {
                    payRespDTO.setDiscountPrice(discountAmountNode.decimalValue());
                } else {
                    payRespDTO.setDiscountPrice(new Money(discountAmountNode.asText()).getAmount());
                }
            }
            if (null != data.get("couponAmount")) {
                JsonNode couponAmountNode = data.get("couponAmount");
                if (couponAmountNode.isBigDecimal()) {
                    payRespDTO.setCouponAmount(couponAmountNode.decimalValue());
                } else {
                    payRespDTO.setCouponAmount(new Money(couponAmountNode.asText()).getAmount());
                }
            }
            if (null != data.get("serviceFee")) {
                JsonNode serviceFeeNode = data.get("serviceFee");
                if (serviceFeeNode.isBigDecimal()) {
                    payRespDTO.setServiceFee(serviceFeeNode.decimalValue());
                } else {
                    payRespDTO.setServiceFee(new Money(serviceFeeNode.asText()).getAmount());
                }
            }
            return payRespDTO;
        }
        return null;
    }

    /**
     * 取消订单
     *
     * @param base 订单参数
     * @return 订单ID
     */
    @SneakyThrows
    @Override
    public boolean cancelOrder(CancelOrderReqDTO base) {
        EPayCancelOrderReqDTO ePayCancelOrderReqDTO = new EPayCancelOrderReqDTO();
        ePayCancelOrderReqDTO.setOutOrderSign(base.getThirdPartyOrderId());
        String url = eGasConfig.getUrl() + CANCEL_URL;
        Map<String, String> map = createBaseMap();
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayCancelOrderReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        signMap.clear();
        log.info("易加油,取消订单: {}", ePayCancelOrderReqDTO);
        final SendLogDTO sendLogDTO = base.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.POST.name());
            sendLogDTO.setReqParam(objectMapper.writeValueAsString(ePayCancelOrderReqDTO));
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayCancelOrderReqDTO);
        build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayCancelOrderReqDTO.freeData();
        return true;
    }

    /**
     * 加油支付回调
     *
     * @param base 回执参数
     * @return 成功 or 失败
     * <p>
     * ResourceAccessException.class 只重试IO异常的情况
     * * maxAttempts 最大请求次数，默认为3
     * * 时间间隔(秒) 0.5, 1,2 ...
     * </p>
     */
    @Override
    @Retryable(value = ResourceAccessException.class, maxAttempts = 4, backoff = @Backoff(delay = 500L, multiplier = 2))
    @SneakyThrows
    public boolean payNotify(PayNotifyReqDTO base) {
        EPayNotifyReqDTO ePayNotifyReqDTO = new EPayNotifyReqDTO();
        ePayNotifyReqDTO.setOutOrderSign(base.getThirdPartyOrderId())
                .setPayAmount(base.getPayAmount().toString());
        String url = eGasConfig.getUrl() + PAY_NOTIFY_URL;
        Map<String, String> map = createBaseMap();
        map.put("orderSign", base.getOrderId());
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayNotifyReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        signMap.clear();
        log.info("易加油,支付回调: {}", ePayNotifyReqDTO);
        final SendLogDTO sendLog = base.getSendLog();
        if (null != sendLog) {
            sendLog.setUrl(logUrl(url, map));
            sendLog.setReqMethod(HttpMethod.POST.name());
            sendLog.setReqParam(JacksonUtil.getObjectMapper().writeValueAsString(ePayNotifyReqDTO));
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayNotifyReqDTO);
        build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayNotifyReqDTO.freeData();
        return true;
    }

    /**
     * 退款支付回调
     *
     * @param base 退款参数
     * @return 成功 or 失败
     */
    @SneakyThrows
    @Override
    public boolean payRefundNotify(PayRefundNotifyReqDTO base) {
        if (!StringUtils.hasLength(base.getReason())) {
            base.setReason("付错油站了");
        }
        String url = eGasConfig.getUrl() + REFUND_NOTIFY_URL;
        Map<String, String> map = createBaseMap();
        map.put("userPhone", base.getMobile());
        map.put("orderId", base.getOrderId());
        Map<String, Object> signMap = new HashMap<>(1);
        signMap.put("reason", base.getReason());
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        map.put("reason", base.getReason());
        log.info("易加油,退款,orderId:{}", base.getOrderId());
        final SendLogDTO sendLog = base.getSendLog();
        if (null != sendLog) {
            sendLog.setUrl(logUrl(url, map));
            sendLog.setReqMethod(HttpMethod.GET.name());
            sendLog.setReqParam("?reason=" + base.getReason());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, map));
        if (null != jsonNode) {
            final JsonNode data = Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
            /**
             * status	string	1.订单审核中 2.订单已退款 3.审核不通过
             */
            String status = data.get("status").asText();
            String msg = data.get("msg").asText();
            switch (status) {
                case "1":
                case "2":
                    return true;
                case "3":
                    throw new ApiException(msg);
                default:
                    return false;
            }
        }
        return true;
    }

    /**
     * 获取站点列表
     *
     * @return 加油站列表
     */
    @SneakyThrows
    public List<EGasStationDTO> getStations(SendLogDTO sendLogDTO) {
        JsonNode jsonNode = stations(sendLogDTO);
        if (null != jsonNode) {
            return objectMapper.readValue(jsonNode.get("data").toString(), new TypeReference<>() {
            });
        }
        return Collections.emptyList();
    }

    @Override
    public JsonNode stations(SendLogDTO sendLogDTO) {
        String url = eGasConfig.getUrl() + STATIONS_URL;
        Map<String, String> map = createBaseMap();
        map.put("sign", EGasSignUtil.sign(map.get("timestamp")));
        sendLogDTO.setReqMethod(HttpMethod.GET.name());
        sendLogDTO.setUrl(logUrl(url, map));
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        return build(() -> restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, map));
    }

    /**
     * 查询加油站详情
     *
     * @param stationDetailReqDTO 加油站ID
     * @return 详情
     */
    @Override
    public JsonNode getStationById(StationDetailReqDTO stationDetailReqDTO) {
        String url = eGasConfig.getUrl() + STATION_DETAIL_URL;
        Map<String, String> map = createBaseMap();
        map.put("sign", EGasSignUtil.sign(map.get("timestamp")));
        map.put("userPhone", stationDetailReqDTO.getMobile());
        map.put("stationId", stationDetailReqDTO.getStationId());
        log.info("易加油,查询加油站详情: {}", stationDetailReqDTO.getStationId());
        final SendLogDTO sendLogDTO = stationDetailReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.GET.name());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, map));
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }

    /**
     * 下载对账单
     *
     * @param reconciliationReqDTO 查询参数
     * @return 对账单
     */
    @Override
    @SneakyThrows
    public JsonNode reconciliation(ReconciliationReqDTO reconciliationReqDTO) {
        StringBuilder urlBuilder = new StringBuilder(eGasConfig.getUrl() + ORDERS_URL);
        Map<String, String> map = createBaseMap();
        Map<String, String> queryParams = createQueryParams(reconciliationReqDTO, urlBuilder);
        Map<String, Object> signMap = new HashMap<>(queryParams.size());
        for(String key:queryParams.keySet()){
            signMap.put(key,queryParams.get(key));
        }
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        signMap.clear();
        map.putAll(queryParams);
        log.info("易加油,下载对账单: {}", queryParams);

        final SendLogDTO sendLogDTO = reconciliationReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(urlBuilder.toString(), map));
            sendLogDTO.setReqParam(objectMapper.writeValueAsString(queryParams));
            sendLogDTO.setReqMethod(HttpMethod.GET.name());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        JsonNode jsonNode = build(() -> restTemplate.exchange(urlBuilder.toString(), HttpMethod.GET, httpEntity, String.class, map));
        map.clear();
        queryParams.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }


    /**
     * 个人订单查询
     *
     * @param historyOrderReqDTO 查询参数
     * @return 订单列表
     */
    @SneakyThrows
    public JsonNode getHistory(HistoryOrderReqDTO historyOrderReqDTO) {
        String url = eGasConfig.getUrl() + HISTORY_URL;
        Map<String, String> map = createBaseMap();
        map.put("userPhone", historyOrderReqDTO.getMobile());
        EPayHistoryOrderReqDTO ePayHistoryOrderReqDTO = new EPayHistoryOrderReqDTO();
        if (null != historyOrderReqDTO.getStationList() && !historyOrderReqDTO.getStationList().isEmpty()) {
            StringBuilder stations = new StringBuilder();
            for (String s : historyOrderReqDTO.getStationList()) {
                stations.append(s);
                stations.append(",");
            }
            ePayHistoryOrderReqDTO.setStationIds(stations.substring(0, stations.length() - 1));

        }
        Integer page = historyOrderReqDTO.getPageNumber();
        if (null == page || 0 == page) {
            page = 1;
        }
        ePayHistoryOrderReqDTO.setPage(page);
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayHistoryOrderReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap, map.get("timestamp")));
        log.info("易加油,个人订单查询: {}", historyOrderReqDTO.getMobile());
        final SendLogDTO sendLogDTO = historyOrderReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.POST.name());
            sendLogDTO.setReqParam(objectMapper.writeValueAsString(ePayHistoryOrderReqDTO));
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayHistoryOrderReqDTO);
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayHistoryOrderReqDTO.freeData();
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }

    /**
     * 个人优惠券查询
     *
     * @param merchandiseQueryReqDTO 查询参数
     * @return 优惠券列表
     */
    @SneakyThrows
    public JsonNode merchandiseQuery(MerchandiseQueryReqDTO merchandiseQueryReqDTO) {
        String url = eGasConfig.getUrl() + QUERY_URL;
        Map<String, String> map = createBaseMap();
        map.put("userPhone", merchandiseQueryReqDTO.getMobile());
        map.put("sign", EGasSignUtil.sign(map.get("timestamp")));
        log.info("易加油,个人优惠券查询: {}", merchandiseQueryReqDTO.getMobile());
        final SendLogDTO sendLogDTO = merchandiseQueryReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.GET.name());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, map));
        merchandiseQueryReqDTO.freeData();
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }

    /**
     * 发放优惠券
     *
     * @param merchandiseReqDTO 查询参数
     * @return 优惠券列表
     */
    @SneakyThrows
    public JsonNode merchandise(MerchandiseReqDTO merchandiseReqDTO) {
        String url = eGasConfig.getUrl() + MERCHANDISE_URL;
        Map<String, String> map = createBaseMap();
        map.put("userPhone", merchandiseReqDTO.getMobile());
        map.put("sourceId", merchandiseReqDTO.getSourceId());
        map.put("sign", EGasSignUtil.sign(map.get("timestamp")));
        log.info("易加油,发放优惠券: {}", merchandiseReqDTO.getMobile());
        final SendLogDTO sendLogDTO = merchandiseReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.GET.name());
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(new HttpHeaders());
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, map));
        merchandiseReqDTO.freeData();
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }

    /**
     * 兑换优惠券
     *
     * @param merchandiseExchangeReqDTO 查询参数
     * @return 优惠券列表
     */
    @SneakyThrows
    public JsonNode merchandiseExchange(MerchandiseExchangeReqDTO merchandiseExchangeReqDTO) {
        EPayMerchandiseExchangeReqDTO ePayMerchandiseExchangeReqDTO = new EPayMerchandiseExchangeReqDTO()
                .setUserPhone(merchandiseExchangeReqDTO.getMobile())
                .setCode(merchandiseExchangeReqDTO.getCode());

        String url = eGasConfig.getUrl() + MERCHANDISE_EXCHANGE_URL;
        Map<String, String> map = createBaseMap();
        Map<String, Object> signMap = BeanUtil.beanToMap(ePayMerchandiseExchangeReqDTO, false, true);
        map.put("sign", EGasSignUtil.sign(signMap,map.get("timestamp")));
        signMap.clear();
        log.info("易加油,兑换优惠券: {}", ePayMerchandiseExchangeReqDTO);
        final SendLogDTO sendLogDTO = merchandiseExchangeReqDTO.getSendLog();
        if (null != sendLogDTO) {
            sendLogDTO.setUrl(logUrl(url, map));
            sendLogDTO.setReqMethod(HttpMethod.POST.name());
            sendLogDTO.setReqParam(objectMapper.writeValueAsString(ePayMerchandiseExchangeReqDTO));
        }
        HttpEntity<Object> httpEntity = new HttpEntity<>(ePayMerchandiseExchangeReqDTO);
        JsonNode jsonNode = build(() -> restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class, map));
        ePayMerchandiseExchangeReqDTO.freeData();
        map.clear();
        if (null != jsonNode) {
            if (null != sendLogDTO) {
                sendLogDTO.setResponseMsg(jsonNode.toString());
            }
            return Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        }
        return null;
    }
    private JsonNode build(Supplier<ResponseEntity<String>> supplier) {
        return BaseHttpResponseUtil.build(supplier, this::buildEGasResponseDTO).getData();
    }

    private ShellResponseDTO buildEGasResponseDTO(JsonNode jsonNode) {
        if (null != jsonNode) {
            ShellResponseDTO shellResponseDTO = new ShellResponseDTO();
            final JsonNode codeJsonNode = Optional.ofNullable(jsonNode.get("code")).orElseThrow(() -> new ApiException("code不存在,json：" + jsonNode));
            final JsonNode messageJsonNode = Optional.ofNullable(jsonNode.get("msg")).orElseThrow(() -> new ApiException("msg不存在,json：" + jsonNode));
            shellResponseDTO.setErrCode(codeJsonNode.intValue());
            shellResponseDTO.setErrMsg(messageJsonNode.asText());
            shellResponseDTO.setData(jsonNode);
            return shellResponseDTO;
        }
        return null;
    }

    private Map<String, String> createBaseMap() {
        Long timestamp = System.currentTimeMillis();
        Map<String, String> map = new HashMap<>(5);
        map.put("platformName", eGasConfig.getPlatNum());
        map.put("timestamp", timestamp + "");
        return map;
    }


    private String logUrl(String url, Map<String, String> paramMap) {
        String logUrl = url;
        for (String key : paramMap.keySet()) {
            if (logUrl.contains(key)) {
                logUrl = logUrl.replace("{" + key + "}", paramMap.get(key));
            }
        }
        return logUrl;
    }

    private Map<String, String> createQueryParams(ReconciliationReqDTO reconciliationReqDTO, StringBuilder urlBuilder) {
        Map<String, String> params = new HashMap<>(5);
        if (null != reconciliationReqDTO.getPageNumber()) {
            build(urlBuilder);
            urlBuilder.append("pageNumber={pageNumber}");
            params.put("pageNumber", reconciliationReqDTO.getPageNumber() + "");
        }
        if (null != reconciliationReqDTO.getPageSize()) {
            build(urlBuilder);
            urlBuilder.append("pageSize={pageSize}");
            params.put("pageSize", reconciliationReqDTO.getPageSize() + "");
        }
        if (StringUtils.hasLength(reconciliationReqDTO.getOrderId())) {
            build(urlBuilder);
            urlBuilder.append("orderId={orderId}");
            params.put("orderId", reconciliationReqDTO.getOrderId());
        }
        if (StringUtils.hasLength(reconciliationReqDTO.getThirdPartyOrderId())) {
            build(urlBuilder);
            urlBuilder.append("outOrderSign={outOrderSign}");
            params.put("outOrderSign", reconciliationReqDTO.getThirdPartyOrderId());
        }
        if (null != reconciliationReqDTO.getStartTime()) {
            build(urlBuilder);
            urlBuilder.append("startTime={startTime}");
            params.put("startTime", DateUtil.localDateTimeToString(reconciliationReqDTO.getStartTime(), "yyyyMMddHHmmss"));
        }
        if (null != reconciliationReqDTO.getEndTime()) {
            build(urlBuilder);
            urlBuilder.append("endTime={endTime}");
            params.put("endTime", DateUtil.localDateTimeToString(reconciliationReqDTO.getEndTime(), "yyyyMMddHHmmss"));
        }
        return params;
    }

    private static void build(StringBuilder urlBuilder) {
        if (urlBuilder.indexOf("?") == -1) {
            urlBuilder.append("?");
        } else {
            urlBuilder.append("&");
        }
    }

    private CalculatePriceDTO buildCalculatePrice(BigDecimal originalPrice, JsonNode jsonNode) {
        JsonNode dataNode = Optional.ofNullable(jsonNode.get("data")).orElseThrow(() -> new ApiException("data为空"));
        CalculatePriceDTO calculatePriceDTO = new CalculatePriceDTO();
        Optional.ofNullable(dataNode.get("oilMass")).ifPresent(oilMassNode -> calculatePriceDTO.setOilVolume(new Money(oilMassNode.asText()).getAmount()));
        Optional.ofNullable(dataNode.get("payAmount")).ifPresent(payAmountNode -> calculatePriceDTO.setCalculatePrice(new Money(payAmountNode.asText()).getAmount()));
        Optional.ofNullable(dataNode.get("discountAmount")).ifPresent(discountAmountNode -> calculatePriceDTO.setDiscountAmount(new Money(discountAmountNode.asText()).getAmount()));
        Optional.ofNullable(dataNode.get("serviceFee")).ifPresent(serviceFeeNode -> calculatePriceDTO.setServiceFee(new Money(serviceFeeNode.asText()).getAmount()));
        Optional.ofNullable(dataNode.get("couponAmount")).ifPresent(couponAmountNode -> calculatePriceDTO.setCouponAmount(new Money(couponAmountNode.asText()).getAmount()));
        calculatePriceDTO.setPrice(originalPrice);
        buildEGasDiscount(calculatePriceDTO);
        return calculatePriceDTO;
    }

    private void buildEGasDiscount(CalculatePriceDTO calculatePrice) {
        Money allDiscountAmount = new Money();
        //不为空且值大于0
        if (null != calculatePrice.getCouponAmount() && calculatePrice.getCouponAmount().compareTo(BigDecimal.ZERO) > 0) {
            allDiscountAmount = allDiscountAmount.add(new Money(calculatePrice.getCouponAmount()));
            calculatePrice.setRefuelingAmount(calculatePrice.getCalculatePrice());
            calculatePrice.addCalculatePriceDetailDTO(new CalculatePriceDetailDTO()
                    .setCalculatePrice(calculatePrice.getCalculatePrice())
                    .setPrice(calculatePrice.getPrice())
                    .setDiscountAmount(calculatePrice.getCouponAmount())
                    .setPlatform(PandaConstant.PLATFORM_E_GAS)
                    .setServiceFee(calculatePrice.getServiceFee())
                    .setPreferentialCategory(PreferentialCategoryEnum.PREFERENTIAL_3.getCode()));
        }
        //不为空且值大于0
        if (null != calculatePrice.getDiscountAmount() && calculatePrice.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
            allDiscountAmount = allDiscountAmount.add(new Money(calculatePrice.getDiscountAmount()));
            calculatePrice.setRefuelingAmount(calculatePrice.getCalculatePrice());
            calculatePrice.addCalculatePriceDetailDTO(new CalculatePriceDetailDTO()
                    .setCalculatePrice(calculatePrice.getCalculatePrice())
                    .setPrice(calculatePrice.getPrice())
                    .setDiscountAmount(calculatePrice.getDiscountAmount())
                    .setPlatform(PandaConstant.PLATFORM_E_GAS)
                    .setServiceFee(calculatePrice.getServiceFee())
                    .setPreferentialCategory(PreferentialCategoryEnum.PREFERENTIAL_1.getCode()));
        }
        calculatePrice.setDiscountAmount(allDiscountAmount.getAmount());
    }
}